package org.colour.amber.rest;

import io.swagger.v3.oas.annotations.responses.ApiResponse;
import java.net.URI;
import lombok.extern.slf4j.Slf4j;
import org.colour.amber.logic.ConfigPublisher;
import org.colour.amber.rest.api.RestResponse;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.async.DeferredResult;
import org.springframework.web.util.UriComponentsBuilder;

/**
 * @author jasonfu
 * @date 2024/9/25 09:48
 */
@Slf4j
@RestController
@RequestMapping(ConfigV2RestController.PATH_CONFIG_V2)
public class ConfigV2RestController {

    public static final String PATH_CONFIG_V2 = "/api/v2/config";

    ConfigPublisher configPublisher;

    public ConfigV2RestController(ConfigPublisher configPublisher) {
        this.configPublisher = configPublisher;
    }

    /**
     * 通过配置ID获取配置信息的接口
     *
     * 此方法主要用于根据配置ID检索相应的配置内容它接受一个配置ID作为路径变量，
     * 日志记录配置ID后，从配置发布服务中获取与该ID对应的配置信息并返回
     *
     * @param configId 配置id，用于标识特定的配置信息
     * @return 包含配置信息的RestResponse对象，表示操作成功和配置内容
     */
    @GetMapping("/{configId}")
    public RestResponse<String> getByConfigId(@PathVariable String configId) {
        log.info("configId={}", configId);
        return RestResponse.success(configPublisher.getByConfigId(configId));
    }

    /**
     * 配置的监听器
     * 该方法用于添加一个配置监听器，当配置发生变化时，监听器会收到通知
     * 如果在指定时间内配置没有发生变化，则返回一个304状态码，表示配置未修改
     *
     * @param configId 配置id，用于标识需要监听的配置
     * @return 返回一个DeferredResult对象，该对象会在配置发生变化时或超时后返回相应结果
     *         超时之后会返回 304 告诉客户端配置未发生变化
     */
    @PostMapping("/{configId}/listener")
    public DeferredResult<ResponseEntity<RestResponse<String>>> addListener(@PathVariable String configId) {
        log.info("configId={}", configId);
        DeferredResult<ResponseEntity<RestResponse<String>>> configListener = new DeferredResult<>(10000L);
        // 添加监听器，当配置发生变化时，configListener 会被调用
        configPublisher.addConfigListener(configId, configListener);
        // 10 秒后，写入 304，表示配置未发生变化
        configListener.onTimeout(() -> {
            // 移除监听器，因为已经超时，不再需要监听
            configPublisher.removeConfigListener(configId, configListener);
            // 设置结果为 304 状态码，表示配置未修改
            configListener.setResult(
                    ResponseEntity.status(HttpStatus.NOT_MODIFIED).build());
        });
        // 返回监听器对象，客户端可以基于此获取配置变更通知或超时通知
        return configListener;
    }
    /**
     * 发布配置
     *
     * 该方法用于发布一个新的配置。通过接收配置的ID和值，将新的配置信息发布到系统中，并且在发布完成后，
     * 重定向到一个特定的URL。
     *
     * @param configId 配置id，用于标识配置的唯一性
     * @param configValue 配置的值，包含了配置的具体内容
     * @param uriBuilder 用于构建配置发布之后重定向的URL，以确保客户端可以访问到新的配置
     * @return 返回一个响应实体，其中包含配置的值，用于确认配置已成功发布
     */
    @PostMapping
    @ApiResponse(responseCode = "201")
    public ResponseEntity<RestResponse<String>> pushConfig(
            @RequestParam(name = "configId") String configId,
            @RequestParam(name = "configValue") String configValue,
            UriComponentsBuilder uriBuilder) {
        // 记录接收到的配置id和配置值的日志信息
        log.info("configId={}|configValue={}", configId, configValue);

        // 构建配置发布完成后的重定向URL
        URI uri = uriBuilder.path(PATH_CONFIG_V2 + "/" + configId).build().toUri();
        // 记录重定向URL的日志信息
        log.info("uri={}", uri);

        // 同步推送配置
        configPublisher.pushConfig(configId, configValue);
        // 异步推送配置
        configPublisher.asyncPublishConfig(configId, configValue);

        // 返回创建了资源的响应，包含配置值作为成功响应的内容
        return ResponseEntity.created(uri).body(RestResponse.success(configValue));
    }
}
